fix(table): dispatcher cold-start, live run counter, smooth typewriter#4675
Conversation
…ains The trigger.dev table-run-dispatcher spent ~6s in module init before its first batchTriggerAndWait — it imports lib/table/service for getTableById, which eagerly imported lib/table/trigger → @/lib/webhooks/processor → webhook-execution + executor, dragging the entire workflow-execution stack into the dispatcher container even though it never fires a trigger. - trigger.ts lazy-imports the webhook processor + polling utils inside fireTableTrigger (the only consumer), so importing service no longer pulls the executor. - buildEnqueueItems only imports the cell job (for the inline `runner`) on the database backend; the trigger.dev backend triggers by task id and ignores runner.
The "X running" badge, per-row gutter Stop button, and runningByRowId map stayed at zero after clicking Run until a manual refetch. useRunColumn optimistically stamped cells pending in the rows cache but never bumped the activeDispatches counter — so when the dispatcher's real pending SSE arrived, applyCell saw the cell was already in-flight (wasInFlight === isInFlight) and skipped the counter delta. The optimistic stamp ate the transition. - onMutate now bumps runningCellCount / runningByRowId by the cells it stamps, snapshotting prior run-state for rollback on error. - onSuccess seeds the dispatch into the overlay list from the response instead of invalidating activeDispatches (a refetch would reset the optimistic counter to the server's still-zero count before the dispatcher stamps).
… smooth The character-by-character reveal used a per-cell setInterval. When many cells reveal at once (a Run-all completing in waves), the independent interval callbacks fire at uncoordinated times and each forces its own render + layout/paint — O(cells) reflows over an un-virtualized grid, so it degrades as more cells fill. Switch to requestAnimationFrame: all cells' callbacks run before one paint, so React batches them into a single render + paint per frame regardless of cell count. Reveal length is derived from elapsed time, so a dropped frame catches up instead of slowing the animation.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview On the client, On the server, Reviewed by Cursor Bugbot for commit 745a5a3. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThree targeted bug fixes following the chunked-dispatcher merge: a dispatcher cold-start reduction via lazy imports, an optimistic live-counter fix for the "X running" badge, and a
Confidence Score: 3/5Safe to merge for the cold-start and typewriter fixes; the live-counter change has a gap where the counter can become permanently stuck if the server returns success without a dispatchId. The onSuccess handler replaces invalidateQueries with a targeted cache update, but returns early without any counter cleanup when dispatchId is undefined. In that scenario the optimistic runningCellCount bump from onMutate has no rollback path — onError won't fire (the mutation succeeded) and no SSE will arrive for a dispatch that was never created, leaving the badge stuck. The lazy-import and rAF changes are straightforward and carry no meaningful risk. apps/sim/hooks/queries/tables.ts — specifically the onSuccess handler in useRunColumn Important Files Changed
Sequence DiagramsequenceDiagram
participant UI as UI (useRunColumn)
participant QC as QueryClient Cache
participant API as Run Column API
participant SSE as SSE Stream
UI->>QC: onMutate: stamp cells pending, bump runningCellCount optimistically
UI->>API: POST /run-column
API-->>UI: "200 { dispatchId }"
alt dispatchId present
UI->>QC: onSuccess: upsert ActiveDispatch into cache (counter preserved)
else dispatchId absent
UI->>UI: return early — counter NOT rolled back
end
SSE-->>QC: cell events update counter as dispatcher processes rows
Note over UI,SSE: onError rolls back both row snapshots and counter bump
Reviews (1): Last reviewed commit: "fix(table): drive cell typewriter with r..." | Re-trigger Greptile |
useRunColumn.onSuccess returned early on a null dispatchId (no matching groups / eligible rows) without undoing the onMutate counter bump — and no SSE would arrive to correct it, leaving the counter permanently inflated. Restore the pre-mutation run-state on that path, mirroring onError.
fa34ae7 to
745a5a3
Compare
Summary
Three follow-up fixes after the chunked-dispatcher merge (#4672), each in its own commit.
table-run-dispatcherimportedlib/table/serviceforgetTableById, which eagerly pulledlib/table/trigger→@/lib/webhooks/processor→ the whole workflow-executor stack into the dispatcher container.trigger.tsnow lazy-imports the webhook processor insidefireTableTrigger, andbuildEnqueueItemsonly imports the cell job (for the inlinerunner) on the database backend — the trigger.dev backend triggers by task id and ignoresrunner.useRunColumnoptimistically stamped cellspendingin the rows cache but never bumpedrunningCellCount/runningByRowId, so the dispatcher's realpendingSSE saw nowasInFlighttransition and the counter stayed at 0 until a refetch ("shows up on refresh").onMutatenow bumps the counter for the cells it stamps (with rollback on error), andonSuccessseeds the dispatch into the overlay from the response instead of refetching (which reset the counter to the server's still-zero count).setIntervalcallbacks fired uncoordinated → O(cells) independent reflows over an un-virtualized grid. Switched torequestAnimationFrame(frame-batched, elapsed-time based) so all reveals render+paint once per frame.Type of Change
Testing
Tested manually in staging (issues observed there); tsc, vitest (lib/table + hooks/queries, 202 passing), lint, and
check:api-validation:strictall pass. Cold-start improvement diagnosed via trigger.dev run traces (dispatcher init dropped from ~8s).Checklist